<template>
    <el-main class="layout-tests">
        <div class="headers-wrapper">调试预览</div>
        <div class="history-wrapper">
            <el-scrollbar ref="scrollbarRef" height="100%">
                <div ref="innerRef" class="px-7">
                    <chatting-message
                        v-if="formData.welcome_text"
                        type="answer"
                        :recordId="0"
                        :avatar="formData.logo"
                        :content="formData.welcome_text"
                        :createTime="currentTime"
                    />

                    <chatting-message
                        v-for="(item, index) in historyList"
                        :key="index"
                        :loading="item.loading"
                        :recordId="item.id"
                        :type="item.type"
                        :avatar="item.avatar"
                        :content="item.content"
                        :context="item.context"
                        :thinks="item.thinks"
                        :quotes="item.quotes"
                        :issues="item.issues"
                        :attach="item.attach"
                        :annex="item.annex"
                        :webSearch="item.web_search"
                        :toolsList="item.tools_list"
                        :taskTime="item.task_time"
                        :createTime="item.create_time"
                        :chatModel="item.chat_model"
                        :isShowCopy="true"
                        :isShowShare="true"
                        :isShowVoice="broadcastSwitch"
                        :isShowTimed="!!formData.is_show_timed"
                        :isShowQuote="!!formData.is_show_quote"
                        :isShowContext="!!formData.is_show_context"
                        :isContextClear="!!item.end_clear"
                        :isShowRewrite="index === historyList.length - 1"
                        :isShowiSsues="index === historyList.length - 1"
                        :isShowChatModel="true"
                        :broadcast="formData.broadcast"
                        @rewrite="rewriteChat"
                        @search="handleSearch"
                        @stop="streamReader?.cancel()"
                        @ask="sendAsk"
                    />
                </div>
            </el-scrollbar>
        </div>

        <div class="my-[15px]">
            <chatting-input-box
                ref="chatActionRef"
                :loading="isReceiving"
                :maxUploadNum="formData.max_upload_num"
                :isUploadFiles="!!formData.is_upload_files"
                :isUploadImage="!!formData.is_upload_image"
                :isWebSearch="!!formData.is_web_search"
                :isShowClear="true"
                @search="(e: boolean) => webSearchOpen = e"
                @clear="handleClear"
                @sending="sendChats"
            />
        </div>

        <el-drawer
            v-model="webSearchShow"
            :with-header="false"
        >
            <chatting-web-search
                :lists="webSearchList"
                @close="webSearchShow = false"
            />
        </el-drawer>
    </el-main>
</template>

<script setup lang="ts">
import { ElScrollbar } from 'element-plus'
import { useVModel, useElementSize } from '@vueuse/core'
import useAppStore from '~/stores/app'
import useUserStore from '~/stores/user'
import datetimeUtil from '~/utils/datetime'
import chatDialogApi from '~/api/agent/chat/dialog'

const props = defineProps<{
    modelValue: Record<string, any>
}>()

const emits = defineEmits<{
    (event: 'update:modelValue', value: Record<string, any>): void
}>()

const formData = useVModel(props, 'modelValue', emits)

const appStore = useAppStore()
const userStore = useUserStore()

// 当前时间
const currentTime = datetimeUtil.datetime()

// 联网搜索
const webSearchOpen = ref<boolean>(false)
const webSearchShow = ref<boolean>(false)
const webSearchList = ref<ChatRecordsWebSearch[]>([])

// 播放开关
const broadcastSwitch = computed(() => {
    return !!(
        appStore.config.switch.broadcast &&
        formData.value.broadcast.model
    )
})

// 对话列表
const historyList = ref<ChatRecordsListResponse[]>([])

// 节点Ref
const chatActionRef = shallowRef()
const scrollbarRef = ref<InstanceType<typeof ElScrollbar>>()
const innerRef = ref<HTMLDivElement>()

// 正在接收
const isReceiving = ref<boolean>(false)
// 流式对象
let streamReader: ReadableStreamDefaultReader<Uint8Array> | null = null

/**
 * 获取对话记录
 */
const queryHistoryList = async () => {
    historyList.value = await chatDialogApi.history({
        sid: 0,
        bot: formData.value.code,
        scene: 'test',
    })

    await nextTick(() => {
        scrollToBottom()
    })
}

/**
 * 联网搜索显示
 */
const handleSearch = (data: any[]) => {
    webSearchShow.value = !webSearchShow.value
    webSearchList.value = data
}

/**
 * 清空对话上下文
 */
const handleClear = async () => {
    if (isReceiving.value) {
        feedback.msgError('请耐心等待回复完毕再操作!')
    } else {
        feedback.confirm('确定要清空当前对话记录吗?')
            .then(async () => {
                await chatDialogApi.clear({
                    sid: 0,
                    scene: 'test',
                    bot: formData.value.code
                })
                const chatNum = historyList.value.length
                if (chatNum > 0) {
                    historyList.value[chatNum - 1].end_clear = 1
                }
                feedback.msgSuccess('历史对话已清空!')
                await nextTick(() => {
                    scrollToBottom()
                })
            }).catch(() => {})
    }
}

/**
 * 重新回答问题
 */
const { lockFn: rewriteChat } = useLockFn(async () => {
    const last = historyList.value[historyList.value.length - 1]
    const userInput = historyList.value.find(({ id }) => id === last.id)
    if (userInput) {
        historyList.value.splice(historyList.value.length - 2, 2)
        await sendChats(
            userInput.content,
            [{'role': 'user', 'content': userInput.content}],
            userInput.annex || []
        )
    }
})

/**
 * 发起建议提问
 */
const sendAsk = async (question: string) => {
    await sendChats(
        question,
        [{'role': 'user', 'content': question}],
        []
    )
}

/**
 * 发送流式对话
 */
const sendChats = async (question: string, messages: any[], files: any[]) => {
    if (isReceiving.value) {
        feedback.msgWarning('请等回复后再发送哦~')
        return
    }

    const datetime = datetimeUtil.datetime()
    historyList.value.push({
        id: 0,
        type: 'question',
        avatar: userStore.user.avatar,
        annex: files,
        content: question,
        create_time: datetime
    })

    historyList.value.push({
        id: 0,
        type: 'answer',
        avatar: formData.value.logo,
        thinks: '',
        content: '',
        web_search: [],
        tools_list: [],
        create_time: datetime,
        loading: true
    })

    isReceiving.value = true
    try {
        await chatDialogApi.sendChat({
            sid: 0,
            bot: formData.value.code,
            scene: 'test',
            stream: true,
            search: webSearchOpen.value,
            messages: messages
        }, {
            onstart(reader) {
                chatActionRef.value?.setInputValue()
                streamReader = reader
            },
            onmessage(value) {
                value.trim().split('data:').forEach(async (text) => {
                    if (text !== '' && text !== '[DONE]') {
                        try {
                            const dataJson = JSON.parse(text)
                            if (dataJson.error) {
                                feedback.msgError(dataJson.error.message)
                            }

                            const { id, event, choices } = dataJson
                            const finish = choices[0]?.finish_reason
                            const answer = choices[0]?.delta.content
                            const thinks = choices[0]?.delta.reasoning_content
                            const toolCalls = choices[0]?.delta.tool_calls

                            let chatIndex = historyList.value.findIndex((item: any) => item.id === id)

                            if (chatIndex === -1) {
                                chatIndex = historyList.value.length - 1
                                if (id) {
                                    historyList.value[chatIndex].id = id
                                }
                            }

                            if (thinks) {
                                historyList.value[chatIndex].thinks += thinks
                            }

                            switch (event) {
                                case 'chat':
                                    if (answer) {
                                        historyList.value[chatIndex].content += answer
                                    }
                                    break
                                case 'tools':
                                    toolCalls.forEach((t: ChatRecordsToolsList) => {
                                        historyList.value[chatIndex].tools_list?.push(t)
                                    })
                                    break
                                case 'search':
                                    const search = answer ? JSON.parse(answer) : null
                                    historyList.value[chatIndex].web_search = search
                                    break
                                case 'image':
                                case 'video':
                                case 'audio':
                                case 'file':
                                    const results = JSON.parse(answer)
                                    if (Array.isArray(results)) {
                                        results.forEach((item) => {
                                            historyList.value[chatIndex].attach?.push(item)
                                        })
                                    } else {
                                        historyList.value[chatIndex].attach?.push(results)
                                    }
                                    break
                            }

                            if (finish === 'normal') {
                                historyList.value[chatIndex].loading = false
                            }
                        } catch {}
                    }
                })
            },
            onclose() {
                isReceiving.value = false
                scrollToBottom()
                setTimeout(queryHistoryList, 1500)
            }
        })
    } catch {
        isReceiving.value = false
        streamReader?.cancel()
    }

    await nextTick(() => {
        scrollToBottom()
    })
}

/**
 * 滚动条到底部
 */
const scrollToBottom = () => {
    const scrollHeight: number | any = scrollbarRef.value?.wrapRef?.scrollHeight
    const clientHeight: number | any = scrollbarRef.value?.wrapRef?.clientHeight
    const scrollH = scrollHeight - clientHeight
    scrollbarRef.value?.setScrollTop(scrollH)
}

/**
 * 监听高度变化
 */
const { height } = useElementSize(innerRef)
watch(height, () => {
    if (isReceiving.value) {
        scrollToBottom()
    }
})

watch(
    () => formData.value.code,
    async (val) => {
        if (val) {
            await queryHistoryList()
        }
    }, { immediate: true }
)
</script>

<style scoped lang="scss">
.layout-tests {
    display: flex;
    flex-direction: column;
    margin-left: 10px;
    background-color: var(--el-bg-color);
    border: 1px solid var(--el-border-color-lighter);
    border-radius: 8px;
    .headers-wrapper {
        font-size: 20px;
        font-weight: 700;
    }
    .history-wrapper {
        flex: 1;
        overflow: hidden;
    }
}
</style>
